package flexo.file.reader;

import java.io.BufferedReader;
import java.io.IOException;

import flexo.file.*;

/**
 * Parses a shader and loads the uniform variables as properties.
 * All the variables must be at the top of the file.
 * Comments are allowed, using '//' token like in C.
 */
public class ShaderParam extends Reader
{
	enum Token
	{
		EOF,
		STRING,
		SEMICOLON,
		ERROR
	}
	
	protected BufferedReader sr;

	protected String lexeme;
	protected char current;
	protected boolean eof;
	protected Token token;
	protected int line;
	
	protected void readChar() throws Exception
	{
		boolean comment = false;
		do
		{
			int temp = sr.read();
			if (temp == -1)
			{
				eof = true;
				return;
			}
			current = (char)temp;
			if (current == '\n')
			{
				++line;
				comment = false;
			}
			else if (current == '/')
			{
				comment = true;
			}
		} 
		while (Character.isWhitespace(current) || comment);		
	}
	
	public Token lexNextToken() throws Exception
	{
		char character = current;

		if (eof)
		{
			return Token.EOF;
		}

		readChar();
		
		if (character == ';')
		{
			return Token.SEMICOLON;
		}

		if (Character.isLetterOrDigit(character))
		{
			lexeme = String.valueOf(character);
			while (Character.isLetterOrDigit(current))
			{
				lexeme += current;
				try
				{
					current = (char)sr.read();
				} catch (IOException e)
				{
					e.printStackTrace();
				}
			}
			if (Character.isWhitespace(current))
			{
				readChar();
			}
			return Token.STRING;
		}
		return Token.ERROR;
	}
	
	protected void nextToken() throws Exception
	{
		token = lexNextToken();
	}

	protected void match(Token token) throws Exception
	{
		if (this.token != token) throw new Exception();
		nextToken();
	}

	public void parse(Node node) throws Exception
	{
		line = 1;
		nextToken();
		while (token != Token.EOF && !eof)
		{
			fill(node);
		}
	}

	protected void fill(Node node) throws Exception
	{
		// There's only two posibilities (well, three really)
		// A uniform variable that is loaded as a property
		// Or a function or attribute/varying variable that is ignored
				
		// If it's a uniform variable
		if (lexeme.equals("uniform"))
		{
			match(Token.STRING);
			String type = lexeme;
			match(Token.STRING);
			String name = lexeme;
			match(Token.STRING);
			match(Token.SEMICOLON);
			node.setProperty(name, type);
		}
		else if (lexeme.equals("attribute") || lexeme.equals("varying"))
		{
			while(token != Token.SEMICOLON)
			{
				nextToken();
			}
			match(Token.SEMICOLON);
		}
		else // A function
		{
			eof = true;
		}		
	}

	public void read(Node fileNode)
	{
		try
		{
			sr = this.getBufferedReader(fileNode);
			readChar();
			parse(fileNode);
			
			sr.close();
		}
		catch (Exception e)
		{
			formatError(fileNode.name,line);
		}
	}

}
